import crypto from 'crypto'
import axios from 'axios'
import {wechatPayConfig} from '../config/wechat.js'

/**
 * 微信支付工具类
 */
class WechatPay {
  /**
   * 生成随机字符串
   */
  static generateNonceStr(length = 32) {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    let result = ''
    for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length))
    }
    return result
  }

  /**
   * 生成签名（MD5）
   */
  static generateSign(params, apiKey) {
    // 按 key 排序
    const sortedKeys = Object.keys(params).sort()
    const stringA = sortedKeys.map((key) => `${key}=${params[key]}`).join('&')
    const stringSignTemp = `${stringA}&key=${apiKey}`

    // MD5 加密并转大写
    return crypto.createHash('md5').update(stringSignTemp, 'utf8').digest('hex').toUpperCase()
  }

  /**
   * 对象转 XML
   */
  static objToXml(obj) {
    let xml = '<xml>'
    for (const key in obj) {
      if (obj[key] !== undefined && obj[key] !== null) {
        xml += `<${key}><![CDATA[${obj[key]}]]></${key}>`
      }
    }
    xml += '</xml>'
    return xml
  }

  /**
   * XML 转对象
   */
  static xmlToObj(xml) {
    const obj = {}
    const regex = /<(\w+)><!\[CDATA\[(.*?)\]\]><\/\1>/g
    let match

    while ((match = regex.exec(xml)) !== null) {
      obj[match[1]] = match[2]
    }

    return obj
  }

  /**
   * 统一下单（小程序支付）
   */
  static async unifiedOrder(orderData) {
    const {appId, mchId, apiKey} = wechatPayConfig
    const nonceStr = this.generateNonceStr()

    // 构建请求参数
    const params = {
      appid: appId,
      mch_id: mchId,
      nonce_str: nonceStr,
      body: orderData.body, // 商品描述
      out_trade_no: orderData.out_trade_no, // 商户订单号
      total_fee: orderData.total_fee, // 金额（分）
      spbill_create_ip: orderData.spbill_create_ip || '127.0.0.1', // 终端IP
      notify_url: wechatPayConfig.notifyUrl, // 回调地址
      trade_type: 'JSAPI', // 小程序支付
      openid: orderData.openid // 用户 openid
    }

    // 生成签名
    params.sign = this.generateSign(params, apiKey)

    // 转换为 XML
    const xml = this.objToXml(params)

    try {
      // 发送请求
      const response = await axios.post('https://api.mch.weixin.qq.com/pay/unifiedorder', xml, {
        headers: {'Content-Type': 'application/xml'}
      })

      // 解析响应
      const result = this.xmlToObj(response.data)

      if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
        // 返回小程序支付参数
        return {
          success: true,
          data: this.generatePayParams(result.prepay_id)
        }
      } else {
        return {
          success: false,
          error: result.err_code_des || result.return_msg || '统一下单失败'
        }
      }
    } catch (error) {
      console.error('微信支付统一下单失败:', error)
      return {
        success: false,
        error: error.message || '网络请求失败'
      }
    }
  }

  /**
   * 生成小程序支付参数
   */
  static generatePayParams(prepayId) {
    const {appId, apiKey} = wechatPayConfig
    const timeStamp = Math.floor(Date.now() / 1000).toString()
    const nonceStr = this.generateNonceStr()
    const packageStr = `prepay_id=${prepayId}`

    const params = {
      appId,
      timeStamp,
      nonceStr,
      package: packageStr,
      signType: 'MD5'
    }

    // 生成签名
    params.paySign = this.generateSign(params, apiKey)

    return params
  }

  /**
   * 验证支付回调签名
   */
  static verifyNotifySign(data) {
    const {apiKey} = wechatPayConfig
    const sign = data.sign
    delete data.sign

    const calculatedSign = this.generateSign(data, apiKey)
    return sign === calculatedSign
  }

  /**
   * 查询订单
   */
  static async queryOrder(outTradeNo) {
    const {appId, mchId, apiKey} = wechatPayConfig
    const nonceStr = this.generateNonceStr()

    const params = {
      appid: appId,
      mch_id: mchId,
      out_trade_no: outTradeNo,
      nonce_str: nonceStr
    }

    params.sign = this.generateSign(params, apiKey)
    const xml = this.objToXml(params)

    try {
      const response = await axios.post('https://api.mch.weixin.qq.com/pay/orderquery', xml, {
        headers: {'Content-Type': 'application/xml'}
      })

      const result = this.xmlToObj(response.data)

      if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
        return {
          success: true,
          data: result
        }
      } else {
        return {
          success: false,
          error: result.err_code_des || result.return_msg || '查询订单失败'
        }
      }
    } catch (error) {
      console.error('查询订单失败:', error)
      return {
        success: false,
        error: error.message || '网络请求失败'
      }
    }
  }

  /**
   * 申请退款
   */
  static async refund(refundData) {
    const {appId, mchId, apiKey} = wechatPayConfig
    const nonceStr = this.generateNonceStr()

    const params = {
      appid: appId,
      mch_id: mchId,
      nonce_str: nonceStr,
      out_trade_no: refundData.out_trade_no, // 商户订单号
      out_refund_no: refundData.out_refund_no, // 商户退款单号
      total_fee: refundData.total_fee, // 订单金额（分）
      refund_fee: refundData.refund_fee, // 退款金额（分）
      refund_desc: refundData.refund_desc || '订单退款' // 退款原因
    }

    params.sign = this.generateSign(params, apiKey)
    const xml = this.objToXml(params)

    try {
      // 注意：退款接口需要使用商户证书
      const response = await axios.post('https://api.mch.weixin.qq.com/secapi/pay/refund', xml, {
        headers: {'Content-Type': 'application/xml'}
        // 这里需要配置证书，实际使用时需要添加证书配置
      })

      const result = this.xmlToObj(response.data)

      if (result.return_code === 'SUCCESS' && result.result_code === 'SUCCESS') {
        return {
          success: true,
          data: result
        }
      } else {
        return {
          success: false,
          error: result.err_code_des || result.return_msg || '申请退款失败'
        }
      }
    } catch (error) {
      console.error('申请退款失败:', error)
      return {
        success: false,
        error: error.message || '网络请求失败'
      }
    }
  }
}

export default WechatPay
